package com.gzt.bookcatalog.service;

import com.gzt.bookcatalog.dto.BookRequestDTO;
import com.gzt.bookcatalog.dto.BookResponseDTO;
import com.gzt.bookcatalog.dto.BookSearchDTO;
import com.gzt.bookcatalog.event.BookEvent;
import com.gzt.bookcatalog.exception.BookNotFoundException;
import com.gzt.bookcatalog.exception.DuplicateIsbnException;
import com.gzt.bookcatalog.factory.BookFactory;
import com.gzt.bookcatalog.mapper.BookMapper;
import com.gzt.bookcatalog.model.Book;
import com.gzt.bookcatalog.repository.BookRepository;
import com.gzt.bookcatalog.service.search.BookSearchStrategy;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.stream.Collectors;

@Validated
@Service
@RequiredArgsConstructor
public class BookService extends BaseService{
    private static final Logger logger = LoggerFactory.getLogger(BookService.class);
    private final BookRepository bookRepository;
    private final BookSearchStrategy bookSearchStrategy;
    private final ApplicationEventPublisher eventPublisher;

    public List<BookResponseDTO> getAllBooks() {
        return bookRepository.findAll()
                .stream()
                .map(BookFactory::createBookResponseDTO)
                .collect(Collectors.toList());
    }

    public BookResponseDTO getBookById(Long id) {
        Book book = findBookById(id);
        return BookMapper.INSTANCE.toDto(book);
    }

    public BookResponseDTO createBook(BookRequestDTO bookRequestDTO) {
        checkDuplicateIsbn(bookRequestDTO.getIsbn());
        Book book = BookFactory.createBook(bookRequestDTO);
        Book savedBook = bookRepository.save(book);
        logger.info("Book created with id: {}", savedBook.getId());
        return BookFactory.createBookResponseDTO(savedBook);
    }

    public BookResponseDTO updateBook(Long id, BookRequestDTO bookRequestDTO) {
        Book existingBook = findBookById(id);
        if (!existingBook.getIsbn().equals(bookRequestDTO.getIsbn())) {
            checkDuplicateIsbn(bookRequestDTO.getIsbn());
        }
        // 将 DTO 的属性更新到原实体上
        BookMapper.INSTANCE.updateBookFromDto(bookRequestDTO, existingBook);
        Book updatedBook = bookRepository.save(existingBook);
        logger.info("Book updated with id: {}", updatedBook.getId());
        eventPublisher.publishEvent(new BookEvent(this, updatedBook));
        return BookMapper.INSTANCE.toDto(updatedBook);
    }

    public void deleteBook(Long id) {
        findBookById(id);
        bookRepository.deleteById(id);
        logger.info("Book deleted with id: {}", id);
    }

    public Page<BookResponseDTO> searchBooks(BookSearchDTO bookSearchDTO) {
         return BookMapper.INSTANCE.toPageDtoList(execute(pageable ->  bookSearchStrategy.search(bookSearchDTO),bookSearchDTO.toPageable()));
    }

    /**
     * 根据 ID 查找书籍，若未找到则抛出异常
     *
     * @param id 书籍 ID
     * @return 书籍实体
     */
    private Book findBookById(Long id) {
        return bookRepository.findById(id)
                .orElseThrow(() -> {
                    logger.error("Book not found with id: {}", id);
                    return new BookNotFoundException("Book not found with id: " + id);
                });
    }

    /**
     * 检查 ISBN 是否重复，若重复则抛出异常
     *
     * @param isbn ISBN 号
     */
    private void checkDuplicateIsbn(String isbn) {
        if (bookRepository.existsByIsbn(isbn)) {
            logger.error("ISBN already exists: {}", isbn);
            throw new DuplicateIsbnException("ISBN already exists: " + isbn);
        }
    }
}